package org.redmagic.service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.redmagic.service.dao.MagicDao;
import org.redmagic.service.domain.HierarchicalRequest;
import org.redmagic.service.utils.MongoProperties;
import org.redmagic.service.utils.PropertyUtils;

import com.google.common.base.Optional;

public class MagicHierarchicalService implements HierarchicalService {

	private final MagicDao magicDao;
	
	public MagicHierarchicalService(MagicDao magicDao) {
		this.magicDao = magicDao;
	}
	
	@Override
	public Map<String, String> getHierarchicalProperties(HierarchicalRequest hierarchicalRequest, String user) {
	
		Map<String, String> resolvedProperties = new HashMap<String, String>();
		
		// attempt to resolve each property
		for (String property : hierarchicalRequest.getProperties()) {
			Optional<String> value = retrieveProperty(property, hierarchicalRequest.getHierarchy(), user);
			
			// if a value was found for the property, add it
			if (value.isPresent()) {
				resolvedProperties.put(property, value.get());
			}
		}		
		
		return resolvedProperties;
	}

	/**
	 * Run each property through the requested hierarchy, looking for the first possible matching value.
	 * 
	 * @param property
	 * 			The property to try to resolve.
	 * @param hierarchy
	 * 			The ordered hierarchy that should be used to resolve the property.
	 * @return
	 * 			An optional containing the value if it was found, or else an Optional.absent();
	 */
	private Optional<String> retrieveProperty(String property, List<List<String>> hierarchy, String user) {
		
		String value = null;
		
		// run through the hierarchy
		for (List<String> tags : hierarchy) {
			Optional<String> result = magicDao.getProperty(property, tags, user);
			
			if (result.isPresent()) {
				// resolve nested properties
				value = resolveNestedProperties(result.get(), hierarchy, user);
			}
		}
		
		return Optional.fromNullable(value);
	}
	
	private String resolveNestedProperties(String value, List<List<String>> tagsHierarchy, String user) {
		
		String rval = value;
		
		String nestedProperty = "";
		while (nestedProperty != null) {

			nestedProperty = PropertyUtils.getNestedProperty(rval);
			
			// check to see if there is a nested property
			if (nestedProperty != null) {
				
				String result = null;
				// loop over the tags hierarchy
				for (List<String> tags : tagsHierarchy) {
					Optional<String> resolvedNestedProperty = magicDao.getProperty(nestedProperty, tags, user);
					
					// if a result was found, replace the $magic{nestedProperty} with the result
					if (resolvedNestedProperty.isPresent()) {
						System.out.println("Resolved \"" + nestedProperty + "\" with tags " + tags + ": " + resolvedNestedProperty.get());
						result = rval.replace(MongoProperties.MAGIC_PREFIX + nestedProperty + MongoProperties.MAGIC_SUFFIX, resolvedNestedProperty.get());
					}
				}
				
				// no results were found for this property, so we fail
				if (result == null) {
					throw new RuntimeException("Unable to resolve value for nested property: " + nestedProperty);
				} else {
					rval = result;
				}
			}
		}
		
		return rval;
	}
}
